Unity着色器教程 | 积雪效果
为游戏中的所有纹理都加上雪花可能需要花费大量时间。本文将展示在Unity中如何创建Image Effect(屏幕空间着色器)来快速改变场景的季节。
请点击【阅读原文】下载着色器,并查看本文涉及的所有源码。使用着色器前后效果图对比如下:
工作原理
上面两张图显示的是同一个场景。它们之间唯一的区别就是第二张图启用了相机上的雪花特效。实现该效果无需对场景的贴图做任何更改。这是什么原理呢?
其实原理非常简单。就是假定所有法线朝上的像素点(如:草,地板等)都需要覆盖雪花。同样,法线朝着其它方向的像素点(如:松树,墙),则需要在雪花纹理和原始纹理之间进行平缓过渡。
获取所需数据
实现上面的雪花效果有以下准备事项:
将渲染路径设置为Deferred(延迟渲染)
将Camera.depthTextureMode设置为DepthNormals
由于第二项可以很方便地由屏幕特效脚本进行设置,所以如果游戏已经使用了前向渲染路径(Forward Rendering Path)时,第一项很容易出问题。
将Camera.depthTextureMode设置为DepthNormals后可以读取屏幕深度(像素与相机之间的距离)和法线(所朝的方向)。
创建一个屏幕特效(Image Effect)由至少一个脚本和一个着色器构成。通常这个着色器不是用来渲染3D物体的,而是根据给定的输入数据渲染一个全屏的图像。在本文的例子中,输入数据就是一张相机渲染的结果图片以及一些用户设置的属性。
这里只是基础的设置,还不能生成雪花。有趣的事情还在后面。
着色器
雪花着色器是无光照着色器(unlit shader),因为屏幕空间是没有光照的,所以也不会用到任何光照信息。片段着色器才是重要部分,通过ScreenSpaceSnow脚本来获取所有数据。脚本代码请点击【阅读原文】查看。
找出需要下雪的地方
正如之前所说,所有法线朝上的表面都将覆盖雪。相机已经设置了生成深度法线贴图,所以现在直接获取即可。
查看Unity官方文档可以了解_CameraDepthNormalsTexture的意义:
深度贴图可以作为一个着色器的全局着色器属性进行采样。通过声明名为_CameraDepthTexture的采样器,就能够采样相机的主深度纹理。
_CameraDepthTexture总是引用相机的主深度贴图。
Unity文档解释深度和法线的数据都打包为16位。所以获取法线需要调用DecodeDepthNormal方法进行解包。这个方法检索的是相机空间的法线。也就是说,如果旋转屏幕相机,那么法线朝向也会改变。脚本中将法线乘以_CamToWorld 矩阵就是为了避免这种情况。它会将法线从相机空间转换为世界空间,这样就不再依赖于相机的透视。
暂时渲染为RGB图像。在Unity中,Y轴是默认向上的。图中绿色部分表示Y坐标轴的值。目前为止结果不错!接下来配置积雪覆盖区域顶部和底部的阀值,以便于调整场景的积雪量。
雪纹理
如果没有纹理,雪看起来会不真实。最难的部分就是将2D纹理(屏幕空间)应用到3D物体上。一种方法是获取像素的世界坐标,然后将世界坐标的X和Z值作为纹理坐标。
这里涉及到一些数学知识,您只需知道vpos是视口坐标,wpos是由视口坐标与_CamToWorld矩阵相乘而得到的世界坐标,并且它通过除以远平面的位置(_ProjectionParams.z)来转换为有效的世界坐标。最后使用XZ坐标乘以可配置参数_SnowTexScale和远平面,来计算雪的颜色并获取适当的值。
合并
下面将积雪与场景进行合并。获取场景原始颜色,并使用snowAmount进行插值渐变为snowColor 。
最后一步:将_TopThreshold设为0.6:
完成!
结论
全屏效果见下图。看起来不错吧?
点击【阅读原文】可查看文中涉及的所有代码,了解教程详细步骤,并下载积雪着色器,您也可以将其用于您的项目!
本文来源于:theknightsofunity.com